
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/markets/macromaps/MacroMap.js
*/
var MacroMap = function() {
	MacroMap.Super(this);
};

MacroMap.Extend(Popup);

MacroMap.prototype.TITLE = "Global macromaps";

MacroMap.prototype.CSS_HIDDEN = "wsodHidden";
MacroMap.prototype.CSS_LOADING = "loading";
MacroMap.prototype.CSS_SELECTED = "selected";
MacroMap.prototype.CSS_WSOD_MODULE = "wsodModule";
MacroMap.prototype.CSS_WSOD_MODULE_CONTENT = "wsodModuleContent";
MacroMap.prototype.CSS_MACROMAP = "macromap";
MacroMap.prototype.CSS_HEADER = "macromapHeader contain";
MacroMap.prototype.CSS_CONTENT = "content";
MacroMap.prototype.CSS_LEGEND = "legend";
MacroMap.prototype.CSS_FOOTER = "footer";
MacroMap.prototype.CSS_RESOURCE = "resource";
MacroMap.prototype.CSS_POSITIVE = "positive";
MacroMap.prototype.CSS_NEGATIVE = "negative";

MacroMap.prototype.MIN_OPACITY = 20;
MacroMap.prototype.MAX_OPACITY = 100;

MacroMap.prototype.DATA_URL = "/ft/markets/data/getMacroMap.asp";

MacroMap.prototype.timeFrames = {
	"Today": 1,
	"5 days": 5,
	"10 days": 10,
	"1 month": 30,
	"3 months": 90,
	"6 months": 180,
	"9 months": 270,
	"1 year": 365
};

MacroMap.prototype.getRequestor = function() {
	var cb = new ContentBuffer();
	this.getRequestor = function() {
		return cb;
	};
	return this.getRequestor();
};

MacroMap.prototype.getSerializer = function() {
	var s = new Serializer();
	this.getSerializer = function() {
		return s;
	};
	return this.getSerializer();
};

// shoe horning popup functionality in after this was alredy developed.
// really only want the positioning logic.
MacroMap.prototype.getFrame = function() {
	return this.elContainer;
};

MacroMap.prototype.init = function() {
	
	this.elMapSelectOptions = [
		Element.create("option", {"value":"indices"}, "Global market indices"),
		Element.create("option", {"value":"currencies"}, "Global currencies")
	];
	
	var helpIcon = Element.create("a", { "class": "icon icon-help", href: "#" }, "Macromap Help");
	var closeIcon = Element.create("div", { "class": "icon icon-delete-close" }, "Close"); 
	var selectMap = Element.create("select", null, this.elMapSelectOptions);
	var adContainer = Element.create("div", {"class":"wsodAdContainer"});
	
	this.elMap = Element.create("div", {"class": this.CSS_RESOURCE });
	this.elSunShade = Element.create("div", {"class": this.CSS_RESOURCE + " sunShade" });
	this.elTimestamp = Element.create("span", { "class": "disclaimer timestamp" }, "");
	
	var elBaseCurrency = Element.create("span", { "class": "disclaimer baseCurrency wsodHidden" }, "<div>&nbsp;</div>Base Currency");
	
	this.showBaseCurrencyLegend = function() {
		Element.removeClass(elBaseCurrency, this.CSS_HIDDEN);
	};
	this.hideBaseCurrencyLegend = function() {
		Element.addClass(elBaseCurrency, this.CSS_HIDDEN);
	};
	
	var elLegendHigh = Element.create("span", { "class": "disclaimer" }, "High");
	var elLegendLow = Element.create("span", { "class": "disclaimer" }, "Change: Low");
	
	this.setLegendText = function(high, low) {
		elLegendHigh.innerHTML = high;
		elLegendLow.innerHTML = low;
	};
	
	var popContainer = Element.get("wsodPop") ? Element.get("wsodPop") : Element.create("div",{id : "wsodPop" }, [], document.body);
	
	this.elContainer = Element.create("div", { "class": this.CSS_MACROMAP }, [
		Element.create("iframe", { src: "javascript:false;", frameborder: 0 }),
		Element.create("div", { "class": this.CSS_WSOD_MODULE_CONTENT }, [
			// header
			Element.create("div", { "class": this.CSS_HEADER }, [
				Element.create("h1", null, this.TITLE),
				adContainer,
				selectMap,
				closeIcon
			]),
			// content
			Element.create("div", { "class": this.CSS_CONTENT }, [
				this.elSunShade,
				Element.create("div", {"class": this.CSS_RESOURCE, id: "regionDividers" }),
				this.elMap,
				Element.create("div", {"class": "loadingOverlay" }, "<span>Loading...</span>")
			]),
			
			// legend
			Element.create("div", { "class": this.CSS_LEGEND }, [
				helpIcon,
				elLegendHigh,
				Element.create("span", { "class": "disclaimer legendKey" }),
				elLegendLow,
				elBaseCurrency,
				this.elTimestamp
			]),
			
			// footer
			Element.create("div", { "class": this.CSS_FOOTER }, [
				this.drawTimeFrameControl(),
				this.drawViewControl()	
			])
		])
	], popContainer);
	
	Element.addClass(this.elContainer, this.CSS_WSOD_MODULE);
	Element.addClass(this.elContainer, this.CSS_HIDDEN);
	
	this.drawAdRequest(adContainer);
	
	Events.add(helpIcon, "click", this.showHelp, this);
	Events.add(closeIcon, "click", this.close, this);
	Events.add(selectMap, "change", this.show, this);
};

MacroMap.prototype.disableFTCallbacks = function() {
	function noop () {
		return "";
	}
	this.drawAdRequest = noop;
	this.initAdRequest = noop; 
	this.addFTWebTrend = noop;
};

// relies on FT's codebase
MacroMap.prototype.addFTWebTrend = function() {
	if (window["dcsMultiTrack"] instanceof Function) {
		window["dcsMultiTrack"](
			"WT.ti", this.getCurrentMap().getName()
		);
	}
};

MacroMap.prototype.drawAdRequest = function(elContainer) {
	/*
	// init macroad Advert
	macroad.init();
	
	var elAd = Element.get(macroad.getSourceDiv());
	
	if (!elAd) {
		return; // safety in case api changes
	}
	
	// set display to block but visibility to hidden so I can grab dimensions
	// prior to positioning
	Element.setStyle(elAd, "visibility: hidden; display: block;");
	
	// get ad size for positioning
	var adSize = Element.getSize(elAd);
	
	// rip ad out of current DOM placement and append it to wsodMacromapAd
	Element.addChild(elContainer, elAd);
	
	// set necessary styles to place this appropriately
	Element.setStyle(elAd, [
		"position: absolute;",
		"top: 50%; left: 50%;",
		"margin-top: ", adSize.height/-2, "px;",
		"margin-left: ", adSize.width/-2, "px;",
		"visibility: visible;"
	].join(""));
	*/	
};

// relies on FT's codebase
var macroad;
MacroMap.prototype.initAdRequest = function() {
	//macroad = new Advert(AD_MACROAD);
};

MacroMap.prototype.getMacroMapHelp = function() {
	var help = new MacroMapHelp();
	help.setMacroMap(this);
	this.getMacroMapHelp = function() {
		return help;
	};
	return this.getMacroMapHelp();
};

MacroMap.prototype.showHelp = function(e, el) {
	e.cancel();
	el.blur();
	
	var help = this.getMacroMapHelp();
	
	help.clearContent();
	this.getCurrentMap().updateHelp(help);
	help.draw();
	
};

MacroMap.prototype.drawHelpScale = function(loss, gain) {
	return [
		"<div class=\"helpLegend\">",
			"<p class=\"loss\">", loss, "</p>",
			"<div class=\"helpScale\"></div>",
			"<p class=\"gain\">", gain, "</p>",
		"</div>"
	].join("");
};

MacroMap.prototype.drawTimeFrameControl = function() {
	var elTimeFrame = Element.create("ul");
	var elItems = [], elSelected;
	this.selectedTimeFrame = "Today";
	
	for (var i in this.timeFrames) {
		var el = Element.create("li", { days: this.timeFrames[i] }, i, elTimeFrame);
		if (i == this.selectedTimeFrame) {
			Element.addClass(el, this.CSS_SELECTED);
			elSelected = el;
		}
		elItems.push(el);
	}
	
	Element.addClass(elItems[elItems.length-1], "last");
	
	var elTimeFrameLabel = Element.create("div", {"class":"label"}); 
	
	this.setTimeFrameLabel = function(value) {
		Element.setHTML(elTimeFrameLabel, value);
	};
	
	Events.add(elItems, "click", function(e, el) {
		Element.removeClass(elSelected, this.CSS_SELECTED);
		Element.addClass(el, this.CSS_SELECTED);
		elSelected = el;
		
		this.getCurrentMap().setTimeFrame(el.getAttribute("days"));
		this.request();
		
	}, this);
	
	return Element.create("div", {"class":"controlGroup timeFrame"}, [
		elTimeFrameLabel,
		elTimeFrame
	]);
};

MacroMap.prototype.drawViewControl = function() {
	var elViewLabel = Element.create("div", {"class":"label"});
	var elViewSelect = Element.create("select");
	
	this.setViewLabel = function(value) {
		Element.setHTML(elViewLabel, value);
	};
	
	this.setViewOptions = function(options, selected) {
		Element.removeChildNodes(elViewSelect);
		
		for (var i=0; i<options.length; i++) {
			if (options[i].getAttribute("value") == selected) {
				options[i].setAttribute("selected", "true")
			}
			
			Element.addChild(elViewSelect, options[i]);	
		}
		
		this.getViewOptions = function() {
			return options;
		};
	};
	
	this.getViewOptions = function() {
		return null;
	};
	
	Events.add(elViewSelect, "change", function() {
		this.getCurrentMap().setView(elViewSelect.value);
		this.request();
	}, this);
	
	return Element.create("div", {"class":"controlGroup view"}, [
		elViewLabel,
		elViewSelect
	]);	
};

MacroMap.prototype.setSunShading = function(shade) {
	this.elSunShade.className = "";
	Element.addClass(this.elSunShade, this.CSS_RESOURCE);
	Element.addClass(this.elSunShade, "sunShade");
	Element.addClass(this.elSunShade, "sunShade-" + shade);
};

MacroMap.prototype.setTimestamp = function(timestamp) {
	this.elTimestamp.innerHTML = timestamp;
};

MacroMap.prototype.show = function(e, el) {
	MacroMap.Super(this, "draw");
	Element.setStyle(this.elMap, "visibility: hidden");  // ie hack
	
	var mm = this;
	setTimeout(function() {
		var map = el.value || el.getAttribute("macromap");
		
		for (var i=0; i<mm.elMapSelectOptions.length; i++) {
			if (map == mm.elMapSelectOptions[i].value) {
				mm.elMapSelectOptions[i].selected = true;
			}
			else {
				mm.elMapSelectOptions[i].selected = false;
			}
		}
		
		if ("currencies" == map) {
			mm.getCurrenciesMap().show();	
		}
		else if ("indices" == map) {
			mm.getIndicesMap().show();
		}
		
	}, 0); // trying the yield to yourself trick
};

MacroMap.prototype.close = function(e, el) {
	this.getMacroMapHelp().close(e, el);
	
	MacroMap.Super(this, "close", arguments);
};

MacroMap.prototype.showLoading = function() {
	Element.addClass(this.elContainer, this.CSS_LOADING);
};

MacroMap.prototype.hideLoading = function() {
	Element.removeClass(this.elContainer, this.CSS_LOADING);
};

MacroMap.prototype.request = function() {
	Element.setStyle(this.elMap, "visibility: hidden");  // ie hack
	
	this.showLoading();
	this.getCurrentMap().request();
};

MacroMap.prototype.load = function(response) {
	this.setSunShading(response.sunShade);
	this.setTimestamp(response.timestamp);
	this.hideLoading();
	
	Element.setStyle(this.elMap, "visibility: visible");  // ie hack
	
	// these next lines are all a terrible IE hack. Need to revisit to see why dom nodes sometimes are not
	// visible without these.
	// of course, they screw up the market indices map, so it only is applied to the currencies map.
	if (this.getCurrentMap() == this.getCurrenciesMap()) {
		var mm = this;
		Element.setStyle(this.elMap, "display: none");  // ie hack	
		
		setTimeout(function() {
			Element.setStyle(mm.elMap, "display: block");  // ie hack
		}, 1);
	}
	
};

MacroMap.prototype.getCurrentMap = function() {
	return false;
};

MacroMap.prototype.getMouseHover = function() {
	var mh = new MouseHover();

	mh.CSS_MOUSE_HOVER = "macromapMouseHover mouseHover";
	
	return mh;
	//this.getMouseHover = function() { return mh; };
	//return this.getMouseHover();
};

MacroMap.prototype.getMapItemClickEvent = function() {
	var clickEvent = Events.add(null, "click", function(e, el) {
		e.cancel();
		
		if (el.getAttribute("localeURL")) {
			window.location = el.getAttribute("localeURL");
		}
		
		//if ("EUR" != el.getAttribute("localeCode")) {
		//	window.location = el.getAttribute("localeURL");
		//}
		//else {
		//	window.location = "/ft/markets/worldEquities.asp";
		//}
	});
	
	this.getMapItemClickEvent = function() {
		return clickEvent;
	};
	
	return this.getMapItemClickEvent();
};

// ------------------------------------------------------------
//	CURRENCY_MACRO_MAP
// ------------------------------------------------------------
MacroMap.prototype.getCurrenciesMap = function() {
	var mm = this; 
	var CSS_BASE = "base";
	
	var regionCoords = {
		EUR: [
			"429,171,430,163,432,163,436,170,433,174,429,171",
			"430,215,399,215,399,203,433,203,450,202,450,190,443,185,477,161,488,163,494,191,483,197,497,211,489,222,444,224,433,221,430,215",
			"511,135,505,122,514,110,508,86,526,78,531,122,523,133,511,135"
		],
		AE: [
			"576,252,597,252,597,268,576,268,576,252",
			"575,199,623,199,623,231,575,231,575,199"
		],
		AR: [
			"293,478,292,449,313,414,314,384,290,374,280,417,277,465,288,481,293,478",
			"366,470,301,470,301,443,366,443,366,470"
		],
		AU: [
			"734,410,730,374,778,346,803,346,833,372,929,372,929,401,832,401,814,436,776,405,734,410"
		],
		BR: [
			"275,341,314,372,315,396,341,412,423,412,423,387,346,387,368,332,320,311,285,314,275,341"
		],
		CA: [
			"111,133,150,182,223,182,261,200,324,187,304,99,261,42,328,42,328,9,233,9,233,40,153,40,110,80,111,133"
		],
		CH: [
			"460,259,381,259,381,231,460,231,460,259"
		],
		CL: [
			"188,439,264,439,264,411,188,411,188,439",
			"288,481,277,465,280,417,290,374,284,362,273,423,271,463,282,480,288,481"
		],
		CN: [
			"639,226,649,227,652,241,669,248,686,244,694,250,694,260,720,272,731,263,736,261,751,242,743,218,773,203,782,185,772,184,758,168,747,171,742,180,736,181,738,189,733,197,714,207,696,204,676,196,670,185,663,186,649,196,648,209,633,215,639,226",
			"954,243,814,243,814,218,954,218,954,243"
		],	
		CZ: [
			"507,305,408,305,408,278,507,278,507,305",
			"482,178,489,184,498,183,500,179,489,174,482,178"
		],
		DK: [
			"366,84,449,84,449,112,366,112,366,84",
			"475,159,484,160,484,154,478,147,473,152,475,159"
		],
		GB: [
			"441,179,459,173,448,142,436,142,436,127,373,127,373,153,429,153,441,179"
		],
		HK: [
			"733,265,731,265,731,263,733,263,733,265",
			"955,318,852,318,852,292,955,292,955,318"
		],
		HU: [
			"584,24,645,24,645,60,584,60,584,24",
			"491,184,511,184,511,207,491,207,491,184"
		],
		ID: [
			"696,311,695,315,711,335,754,342,797,339,796,325,728,309,724,328,711,328,696,311",
			"955,360,890,360,890,333,955,333,955,360"
		],
		IN: [
			"642,300,619,259,634,228,649,227,651,244,666,252,686,244,691,249,682,262,666,267,642,300",
			"695,333,618,333,618,307,695,307,695,333"
		],
		JP: [
			"771,239,771,231,793,220,799,196,810,202,797,226,771,239",
			"955,174,873,174,873,145,955,145,955,174"
		],
		KR: [
			"761,230,762,220,768,217,771,227,761,230",
			"956,209,844,209,844,183,956,183,956,209"
		],
		MX: [
			"110,269,192,269,192,297,110,297,110,269",
			"166,235,215,255,221,271,240,267,228,285,192,271,166,235"
		],
		MY: [
			"698,303,703,314,707,313,704,304,698,303",
			"724,484,619,484,619,458,724,458,724,484",
			"732,308,742,311,744,306,739,302,732,308"
		],
		NO: [
			"367,46,468,46,468,72,367,72,367,46",
			"472,146,484,137,484,116,499,88,513,84,529,75,521,71,497,81,475,118,466,125,467,142,472,146"
		],
		NZ: [
			"841,484,957,484,957,453,881,453,892,421,875,408,875,428,852,444,841,484"
		],
		PL: [
			"488,163,491,174,504,181,510,180,513,173,510,161,497,158,488,163",
			"518,28,576,28,576,56,518,56,518,28"
		],
		RU: [
			"775,200,784,183,774,184,760,166,747,168,738,179,668,180,625,159,598,174,568,180,570,208,545,194,552,179,530,166,522,135,533,123,527,79,622,57,706,9,788,9,788,28,891,28,891,45,762,45,877,76,917,102,910,113,892,108,890,122,856,140,836,174,833,152,855,124,821,138,786,157,804,167,801,192,779,204,775,200"
		],
		ZA: [
			"494,393,501,411,522,405,535,385,528,372,505,382,494,393",
			"579,448,461,448,461,418,579,418,579,448"
		],
		SE: [
			"413,8,502,8,502,35,413,35,413,8",
			"481,140,484,137,484,116,499,88,508,86,512,103,498,120,496,141,493,153,487,158,481,140"
		],
		SG: [
			"771,447,671,447,671,420,771,420,771,447",
			"709,317,706,317,706,314,709,314,709,317"
		],
		TH: [
			"698,303,692,292,693,269,710,277,707,288,698,286,697,292,703,302,698,303",
			"825,312,767,312,767,285,825,285,825,312"
		],
		TR: [
			"507,223,503,213,513,208,518,210,507,223",
			"520,222,518,213,538,207,559,209,563,221,541,226,520,222",
			"575,273,503,273,503,245,575,245,575,273"
		],
		TW: [
			"753,255,745,255,745,265,753,265,753,255",
			"959,280,839,280,839,252,959,252,959,280"
		],
		US: [
			"111,133,110,80,67,71,46,87,51,132,66,154,87,135,111,133",
			"306,193,260,202,224,183,146,183,160,231,215,255,256,254,270,227,306,193",
			"58,220,143,220,143,195,58,195,58,220"
		]/*,
		TEST2: ["468,192,473,186,480,191,474,194,468,192"],
		TEST4: ["964,515,0,515,0,0,964,0,964,515"]*/
	};
	
	var CurrenciesMacroMap = function() {
		var locales = {};
		var baseCurrencyCode = "";
		var timeFrame;
		var mapItems = {};
		
		// the other option is to make the biggest 100%
		var maxValues = {
			"1": .075,
			"5": .25,
			"10": .4,
			"30": 1,
			"90": 2.25,
			"180": 4,
			"270": 5,
			"365": 6
		};
		
		var elContainer, elUnderlays, elOverlays;
		var viewOptions;
		
		var clickEvent = mm.getMapItemClickEvent();
		var mouseHover = mm.getMouseHover();
		
		function getLocale(code, result) {
			if (locales[code]) {
				return locales[code];
			} 
			
			var id = "country-" + code;
			locales[code] = { 
				id: id,
				elUnderlay: Element.create("div", {"class":"resource country underlay " + id}), 
				elOverlay: Element.create("div", {id: id, "class":"resource country " + id})
			}
			
			if (result.name) {
				viewOptions.push(Element.create("option", {"value":result.cc}, result.name));
			}
			
			Element.addChild(elUnderlays, locales[code].elUnderlay);
			Element.addChild(elOverlays, locales[code].elOverlay);
			
			if (mapItems[code]) {
				mouseHover.addTarget(mapItems[code]);
				
				if (result.localeURL) {
					// add click event to send to country overview page.
					Element.setAttribute(mapItems[code], "localeURL", result.localeURL);
					Element.setAttribute(mapItems[code], "href", result.localeURL);
				}
				
				clickEvent.addElement(mapItems[code]);
			}
	
			return locales[code];
		};
		
		function updateLocale(result) {
			var locale = getLocale(result.lc, result);
			
			clearLocaleElements(locale);
			locale.hoverHTML = null;
			
			for (var i in result) {	
				locale[i] = result[i];
			}
			
			if (result.cc == baseCurrencyCode) {
				Element.addClass(locale.elOverlay, CSS_BASE);
				Element.setOpacity(locale.elOverlay, 100);
			}
			else {
				if (false == result.change) {
					// error finding rate, just hide the country?
					Element.addClass(locale.elOverlay, mm.CSS_HIDDEN);
					Element.addClass(locale.elUnderlay, mm.CSS_HIDDEN);
				}
				else if (result.change >= 0) {
					Element.addClass(locale.elOverlay, mm.CSS_POSITIVE);
				}
				else {
					Element.addClass(locale.elOverlay, mm.CSS_NEGATIVE);
				}
				
				var weightedValue = Math.abs(result.change/maxValues[timeFrame])*100;
				Element.setOpacity(locale.elOverlay, Math.min(Math.max(weightedValue, mm.MIN_OPACITY), mm.MAX_OPACITY));
			}
		};
		
		function clearLocaleElements(locale) {
			var els = [locale.elUnderlay, locale.elOverlay];
			//var els = [locale.elOverlay];
			
			Element.removeClass(els, mm.CSS_POSITIVE);
			Element.removeClass(els, mm.CSS_NEGATIVE);
			Element.removeClass(els, mm.CSS_HIDDEN);
			Element.removeClass(els, CSS_BASE);
		};
		
		
		function buildImageMap() {
			var elMap = Element.create("map", { "name": "mapLocales", id: "mapLocales" });
			
			for (var i in regionCoords) {
				region = regionCoords[i];
				mapItems[i] = [];
				
				for (var j=0; j<region.length; j++) {
					mapItems[i].push(Element.create("area", {
						"shape": "poly",
						"href": "#",
						"localeCode": i,
						"coords": region[j]
					}, null, elMap));
				}
			}
			
			return elMap;
		}
		
		this.init = function() {
			// insert CurrencyMacroMap relevant elements.
			// sahould we wipe out all existing market macro map elements?
			// what do I need to do?
			
			elUnderlays = Element.create("div", {"class":mm.CSS_RESOURCE});
			elOverlays = Element.create("div", {"class":mm.CSS_RESOURCE});
			
			elContainer = Element.create("div", { id: "currencyMap", "class": mm.CSS_RESOURCE }, [
				//elUnderlays,
				//Element.create("div", { id: "countryUnderlays", "class": mm.CSS_RESOURCE }),
				buildImageMap(),
				elOverlays,
				Element.create("div", {"class": "resource", id: "currencyLabels" }),
				Element.create("img", {"class": "resource", "useMap": "#mapLocales", src: "/gif/x.gif" })
			]);
			
			viewOptions = [];
			
			this.setView("GBP");
			this.setTimeFrame(1);
		};
		
		function getHoverHTML(locale) {
			if (locale.hoverHTML) {
				return locale.hoverHTML;
			}
			
			var moreLink = (locale.localeURL) ? "<span class=\"moreLink\">Click for more " + locale.localeAdj + " market research and news.</span>" : "";
			
			var name = String(locale.name).replace(/\(.*\)$/, "");
			
			if (baseCurrencyCode != locale.cc) {
				var code = locale.cc + "/" + baseCurrencyCode;
				
				locale.hoverHTML = [
					"<h2 class=\"currencyTitle\">",
						"<span class=\"indexName\">", name, "</span>",
						"<span class=\"country\">(", code, ")</span>",
					"</h2>",
					"<table><tbody>",
						"<tr>",
							"<td class=\"text\">Last</td>",
							"<td>", locale.rateDisplay, "</td>",
						"</tr>",
						"<tr>",
							"<td class=\"text\">Change</td>",
							"<td>", locale.changeDisplay,"</td>",
						"</tr>",
					"</tbody></table>",
					moreLink
				].join("");
			}
			else {
				
				locale.hoverHTML = [
					"<h2 class=\"currencyTitle\">",
						"<span class=\"indexName\">", name, "</span>",
						"<span class=\"country\">(Base Currency)</span>",
					"</h2>",
					moreLink
				].join("");	
			}
			
			return locale.hoverHTML;
		};
		
		mouseHover.getShowEvent().addListener(function(e, el) {
			var code = el.getAttribute("localeCode");
			var locale = getLocale(code);
			
			mouseHover.clearContent();
			mouseHover.getContent().innerHTML = getHoverHTML(locale);
		});
		
		function getHelpContent() {
			var content = [
				"<p class=\"main\">The FT Currency Macromap provides a global summary of currency changes over multiple time periods, all relative to the currency of your choice.</p>",
				"<h3>Viewing the Macromap</h3>",
				"<p>Colors display the relative performance compared to your selected base currency.</p>",
				"<p class=\"baseCurrency\">The base currency's country of origin is indicated through a colored highlight &nbsp;<span class=\"base\">&nbsp;</span></p>",
				mm.drawHelpScale(
					"Currencies weaker than the base currency",
					"Currencies stronger than the base currency"
				),
				"<h3>Changing Macromap Settings</h3>",
				"<p>Settings for the the Currency Macromap include time periods and base currency.</p>",
				"<p>To view currency performance over different time periods, click one of the time periods at the bottom of the macromap.</p>",
				"<p>To change the base currency, select a different currency from the dropdown at the bottom of the macromap.</p>"
			].join("");
			
			getHelpContent = function() {
				return content;
			};
			return getHelpContent();
		};
		
		this.updateHelp = function(help) {
			help.clearContent();
			help.setTitleText("Currency Macromap");
			help.getContent().innerHTML = getHelpContent();
			help.draw();	
		};
		
		this.setView = function(value) {
			baseCurrencyCode = value;
		};
		
		this.setTimeFrame = function(value) {
			timeFrame = value;
		};
		
		this.getName = function() {
			return "Currency Macromaps";
		};
		
		this.show = function() {
			//if (mm.getCurrentMap() != this) {
				mm.showLoading();
				
				setTimeout(function() {
					mm.getCurrentMap = mm.getCurrenciesMap;
					mm.setTimeFrameLabel("Currency Performance");
					mm.setViewLabel("Performance Compared to");
					mm.showBaseCurrencyLegend();
					mm.setLegendText("Gain", "Relative Currency Performance: Loss");
					
					
					Element.removeChildNodes(mm.elMap);
					Element.addChild(mm.elMap, elContainer);
					
					mm.addFTWebTrend();
					mm.request();
				}, 0);
			//}
		};
		
		this.request = function() {
			var params = {
				base: baseCurrencyCode,
				time: timeFrame,
				onload: "this.load"
			};
			
			mm.getRequestor().load({
				url: mm.DATA_URL,
				data: {
					view: "currency",
					params: mm.getSerializer().serialize(params)
				},
				method: "post",
				contentType: "text/javascript",
				context: this
			});
		};
		
		this.load = function(response) {
			response = mm.getSerializer().deserialize(response);
			
			//console.log(response);
			
			for (var i=0; i<response.results.length; i++) {
				updateLocale(response.results[i])
			}
			
			if (mm.getViewOptions() != viewOptions) {
				mm.setViewOptions(viewOptions, baseCurrencyCode);
			}
			
			mm.load(response);
		};
	};
	
	var map = new CurrenciesMacroMap();
		map.init();
	
	this.getCurrenciesMap = function() {
		return map;
	};
	
	return this.getCurrenciesMap();
};

// ------------------------------------------------------------
//	INDICES_MACRO_MAP
// ------------------------------------------------------------
MacroMap.prototype.getIndicesMap = function() {
	var mm = this; 
	var mouseHover = mm.getMouseHover();
	
	var CSS_POSITIVE = "pos";
	var CSS_NEGATIVE = "neg";
	var CSS_CONTRAST = "contrast";
	
	var IndicesMacroMap = function() {
		var timeFrame;
		var regions = {}, elContainer;
		var relativeSizeBuckets = 7;
		var relativeSizeField, relativeSizes = [];
		var view;
		
		var clickEvent = mm.getMapItemClickEvent();
		
		var maxValues = {
			"1": 1,
			"5": 2,
			"10": 3.5,
			"30": 5,
			"90": 6,
			"180": 9,
			"270": 15,
			"365": 20
		};
		
		function getRegion(region) {
			if (regions[region.code]) {
				return regions[region.code];
			}
			
			regions[region.code] = {
				code: region.code,
				name: region.name,
				elIndices: Element.create("div", { 
					"class": "region region-" + region.code
				}, null, elContainer),
				indices: {}
			};
			
			if (region.indices.length > 1) {
				Element.addClass(regions[region.code].elIndices, "multi");
			}
			
			return regions[region.code];
		};
		
		function getIndex(index, region) {
			if (region.indices[index.code]) {
				return region.indices[index.code];
			}
			
			index.elName = Element.create("span", {"class":"name"}, index.name);
			index.elValue = Element.create("span", {"class":"value"});
			
			index.elIndexBg = Element.create("div", {"class":"indexBg"});
			index.elIndexInner = Element.create("div", {"class":"index"}, [
				index.elName, 
				index.elValue
			]);
			
			index.elIndex = Element.create("div", {
					"class":"indexContainer", 
					"regionCode": region.code, 
					"indexCode":index.code, 
					"localeCode": index.localeCode, 
					"localeURL": index.localeURL
			}, [
				index.elIndexBg,
				index.elIndexInner
			], region.elIndices);
			
			if (!index.failure) {
				mouseHover.addTarget(index.elIndex);
				
				if (index.localeURL) {
					// add click event to send to country overview page.
					clickEvent.addElement(index.elIndex);
				}
			}
			
			
			Element.create("br", { "class":"clear"}, null, region.elIndices); // thanks IE6
			
			region.indices[index.code] = index;
			return region.indices[index.code]; 
		};
		
		function updateRegion(result) {
			region = getRegion(result);
			
			for (var i=0; i<result.indices.length; i++) {
				updateIndex(result.indices[i], region);				
			}
		};
		
		function updateIndex(result, region) {
			index = getIndex(result, region);
			
			index.quote = result.quote;
			index.hoverHTML = null;
			
			if (!index.failure) {
				Element.removeClass(index.elValue, mm.CSS_HIDDEN);
				Element.setHTML(index.elValue, result.quote.display);
			}
			else {
				Element.addClass(index.elValue, mm.CSS_HIDDEN);				
			}
			
			Element.removeClass(index.elIndex, CSS_NEGATIVE);
			Element.removeClass(index.elIndex, CSS_POSITIVE);
			Element.removeClass(index.elIndex, CSS_CONTRAST);
			
			if (!index.quote.pctChg) {
				Element.setOpacity(index.elIndexBg, 100);	
			}
			else {
				if (index.quote.pctChg > 0) {
					Element.addClass(index.elIndex, CSS_POSITIVE);
				}
				else if (index.quote.pctChg < 0) {
					Element.addClass(index.elIndex, CSS_NEGATIVE);
				}
				
				var weightedValue = Math.abs(index.quote.pctChg/maxValues[timeFrame])*100;
				
				if (false !== index.quote[relativeSizeField]) {
					relativeSizes.push(Math.abs(index.quote[relativeSizeField]));
				}
				
				if (weightedValue < 40) {
					Element.addClass(index.elIndex, CSS_CONTRAST);
				}
				
				Element.setOpacity(index.elIndexBg, Math.min(Math.max(weightedValue, mm.MIN_OPACITY), mm.MAX_OPACITY));
			}
		};
		
		function getStats() {
			var mean, median, stdDev;
			var sum = 0, max = 0, variance = 0;
			
			relativeSizes = relativeSizes.sort(function(a, b) { 
				return a-b; 
			});
			median = relativeSizes[Math.floor(relativeSizes.length/2)];
			
			for (var i=0, val; i<relativeSizes.length; i++) {
				val = relativeSizes[i];
				max = Math.max(val, max);
				sum += val;
			}
			
			mean = sum / relativeSizes.length;
			for (var i=0, val; i<relativeSizes.length; i++) {
				val = relativeSizes[i];
				
				variance += Math.pow(val - mean, 2); 
			}
			
			return {
				stdDev: Math.sqrt(variance / (relativeSizes.length-1)),
				mean: mean,
				median: median,
				max: max
			}
		};
		
		function getDevDistance(value, stats) {
			return (Math.abs(value)-stats.mean) / stats.stdDev
		};
		
		function updateSizes() {
			var stats = getStats();
			var meanSize = 4, distance = 4;
			var maxDevDistance = getDevDistance(stats.max, stats);
			
			for (var i in regions) {
				var index, leftOffset, devDistance, value, size;
				
				for (var j in regions[i].indices) {
					index = regions[i].indices[j];
					clearSize(index);	
					
					value = index.quote[relativeSizeField];
					
					if (false !== value && undefined !== value) {
						devDistance = getDevDistance(value, stats);
						size = Math.max(1, meanSize + Math.floor((devDistance / maxDevDistance) * 4));
						Element.addClass(index.elIndex, "size-" + Math.min(size, relativeSizeBuckets));
					}
					else {
						Element.addClass(index.elIndex, "size-1");
					}
					
					var size = Element.getSize(index.elIndex);
					
					leftOffset = size.width/2*-1;
					index.elIndex.style.marginLeft = leftOffset + "px";
				
				}
			}
		};
		
		function clearSize(index) {
			for (var i=1; i<=relativeSizeBuckets; i++) {
				Element.removeClass(index.elIndex, "size-" + i);
			}
		};
		
		function getViewOptions() {
			var options = [
				Element.create("option", {"value":"performance"}, "Performance"),
				Element.create("option", {"value":"volume"}, "Relative Volume")
			];
			
			getViewOptions = function() {
				return options;
			};
			
			return getViewOptions();
		};
		
		function getHoverHTML(index) {
			if (index.hoverHTML) {
				return index.hoverHTML;
			}
			
			var volume = "";
			if (index.quote.volumeDisplay) {
				volume = ["<tr>",
					"<td class=\"text\">Volume</td>",
					"<td>", index.quote.volumeDisplay, "</span></td>",
				"</tr>"].join("");
			}
			
			if (index.inSession) {
				var marketStatus = "<span class=\"icon icon-markets-open\">Open</span>";
			}
			else {
				var marketStatus = "<span class=\"icon icon-markets-closed\">Closed</span>";
			}
			
			var moreLink = (index.localeURL) ? "<span class=\"moreLink\">Click for more " + index.localeAdj + " market research and news.</span>" : "";
			
			index.hoverHTML = [
				"<h2>",
					"<span class=\"indexName\">", index.name, "</span>",
					"<span class=\"country\">", index.localeName, "</span>",
					marketStatus,
				"</h2>",
				"<table><tbody>",
					"<tr>",
						"<td class=\"text\">Last</td>",
						"<td>", index.quote.closeDisplay, "</td>",
					"</tr>",
					"<tr>",
						"<td class=\"text\">Change</td>",
						"<td>", index.quote.changeDisplay, " ", index.quote.display, "</span></td>",
					"</tr>",
					volume,
					"<tr>",
						"<td class=\"text\">Currency</td>",
						"<td>", index.currency, "</span></td>",
					"</tr>",
				"</tbody></table>",
				moreLink
			];
			
			
			index.hoverHTML = index.hoverHTML.join("");
			
			return index.hoverHTML;
		};
		
		mouseHover.getShowEvent().addListener(function(e, el) {
			var index = getIndex(
				{ code: el.getAttribute("indexCode") }, 
				getRegion({ code: el.getAttribute("regionCode") }) 
			);
			
			mouseHover.clearContent();
			mouseHover.getContent().innerHTML = getHoverHTML(index);
		});
		
		function getHelpContent() {
			var content = [
				"<p class=\"main\">The FT Market Index Macromap provides a summary of global index performance over different, selectable time periods.</p>",
				"<h3>Viewing the Macromap</h3>",
				"<p>Colors display the relative performance across all of the indices displayed on the map.</p>",
				"<p>Size of each index block represents that index's performance or volume (relative to its average volume).</p>",
				mm.drawHelpScale(
					"Lower index performance relative to others displayed",
					"Higher Index performance relative to others displayed"
				),
				"<h3>Changing Macromap Settings</h3>",
				"<p>Settings for the Market Index Macromap include time periods and size displays.</p>",
				"<p>To view index performance over different time periods, click one of the time periods at the bottom of the macromap.</p>",
				"<p>To change the meaning of index block size, select an option from the dropdown at the bottom of the macromap.</p>"
			].join("");
			
			getHelpContent = function() {
				return content;
			};
			return getHelpContent();
		};
		
		this.updateHelp = function(help) {
			help.clearContent();
			help.setTitleText("Market Index Macromap");
			help.getContent().innerHTML = getHelpContent();
			help.draw();	
		};
		
		this.init = function() {
			elContainer = Element.create("div", {"class": mm.CSS_RESOURCE });
			this.setTimeFrame(1);
			this.setView("performance");
		};
		
		this.getName = function() {
			return "Market Macromaps";
		};
		
		this.setView = function(value) {
			view = value;
			
			if ("performance" == view) {
				relativeSizeField = "pctChg";
			}
			else {
				relativeSizeField = "relVolume";
			}
		};
		
		this.setTimeFrame = function(value) {
			timeFrame = value;
		};
		
		this.show = function() {
			//if (mm.getCurrentMap() != this) {
				mm.showLoading();
				
				setTimeout(function() {
					mm.setTimeFrameLabel("Market Performance");
					mm.setViewLabel("Size displays");
					mm.setViewOptions(getViewOptions());
					mm.hideBaseCurrencyLegend();
					mm.setLegendText("Gain", "Index Performance: Loss");
					
					Element.removeChildNodes(mm.elMap);
					Element.addChild(mm.elMap, elContainer);
					
					mm.getCurrentMap = mm.getIndicesMap;
					mm.addFTWebTrend();
					mm.request();
				}, 0);
			//}
		};
		
		this.request = function() {
			var params = {
				time: timeFrame,
				onload: "this.load"
			};
			
			mm.getRequestor().load({
				url: mm.DATA_URL,
				data: {
					view: "indices",
					params: mm.getSerializer().serialize(params)
				},
				method: "post",
				contentType: "text/javascript",
				context: this
			});
		};
		
		this.load = function(response) {
			response = mm.getSerializer().deserialize(response);
			
			setTimeout(function() {
				relativeSizes = []; 
				
				//console.log(response);
				
				for (var i=0; i<response.results.length; i++) {
					updateRegion(response.results[i]);
				}
				
				setTimeout(function() {
					updateSizes();
					mm.load(response);
					
				}, 0);
			}, 0);
		};
	};
	
	
	var map = new IndicesMacroMap();
		map.init();
	
	this.getIndicesMap = function() {
		return map;
	};
	
	return this.getIndicesMap();
};

MacroMapHelp = function() {
	MacroMapHelp.Super(this);
	Element.addClass(this.getFrame(), "macromapHelp");
};

MacroMapHelp.Extend(Popup);

MacroMapHelp.prototype.setMacroMap = function(map) {
	this.getMacroMap = function() {
		return map;
	}
};

MacroMapHelp.prototype.draw = function() {
	// this is brittle way to do this.
	if (this.isVisible()) {
		return;
	}
	
	var frame = this.getFrame();
	Element.removeClass(frame, this.CSS_HIDDEN);
	this.position();
	
	return true;
};

MacroMapHelp.prototype.position = function () {
    var contentWell = Element.getXY(this.getFrame().offsetParent);
	
    var size = Element.getSize(this.getMacroMap().elContainer); //Element.getViewport();
    var pos = Element.getXY(this.getMacroMap().elContainer);
	var frame = this.getFrame();

    var frameSize = Element.getSize(frame);

    var frameTop = pos.y + ( size.height / 2 ) - ( frameSize.height / 2 ) - contentWell.y;
    var frameLeft = pos.x + ( size.width / 2 ) - ( frameSize.width / 2 );

    Element.setXY(frame, frameLeft, frameTop);
	
	var size = Element.getSize(frame);
	Element.setSize(this.getIframeShim(), size.width, size.height);
	
	// add a class instead
    frame.style.visibility = 'visible';
}
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/markets/ResearchDataArchive.js
*/
ResearchDataArchive = function() {
	this.reports = [];
}

ResearchDataArchive.prototype.REPORT_TYPE_ALL = "ALL";
ResearchDataArchive.prototype.CSS_HIDDEN = "wsodHidden";

ResearchDataArchive.prototype.init = function() {
	this.REPORT = this.REPORT ? this.getSerializer().deserialize(this.REPORT) : {};

	if (Element.get("categorySelectRDA")) {
		this.getCategoryFlyout();
	}
	if (Element.get("reportSelectRDA")) {
		this.getReportFlyout();
		this.updateReports();
	}

	if (this.getCalendarContainer()) {
		this.getEventManager().add(this.getCalendarControls(), "click", this.pageCalendar, this);
	}
}

ResearchDataArchive.prototype.getCalendarContainer = function() {
	var calendarContainer = Element.parseSelector("div.calendarContainer", "researchDataArchiveModule", "first");
	var calendarControls = Element.parseSelector("div.calendarControls a", calendarContainer);
	this.getCalendarControls = function() {
		return calendarControls;
	}
	
	this.getCalendarContainer = function() {
		return calendarContainer
	}
	
	return this.getCalendarContainer();
}

ResearchDataArchive.prototype.getCategories = function() {
	var categories = [];
	for (var i in this.REPORT.CATEGORIES) {
		categories.push({label: this.REPORT.CATEGORIES[i].name, value: this.REPORT.CATEGORIES[i].code});
	}
	
	return categories;
};

ResearchDataArchive.prototype.getReports = function() {
	var reports = [];
	for (var i in this.REPORT.TYPES) {
		reports.push({label: this.REPORT.TYPES[i].title, value: this.REPORT.TYPES[i].code, css: this.REPORT.TYPES[i].category});
	}
	
	return reports;
};

ResearchDataArchive.prototype.getCategoryFlyout = function() {
	var flyout = new Flyout();
	
	flyout.setParent(Element.get("wsod"));
	flyout.addTarget(Element.get("categorySelectRDA"));
	flyout.setData(this.getCategories());
	flyout.onSelect().addListener({
		context: this,
		handler: this.selectCategory
	});	
	
	var flyoutLabel = Element.get("categorySelectRDA").firstChild;
	var flyoutControls = Element.parseSelector("ul li", flyout.getFrame());
	
	this.getCategoryFlyoutLabel = function() {
		return flyoutLabel;
	}
	
	this.getCategoryFlyoutControls = function() {
		return flyoutControls;
	}
	
	this.getCategoryFlyout = function() {
		return flyout;
	}
	
	return this.getCategoryFlyout();
}

ResearchDataArchive.prototype.getReportFlyout = function() {
	var flyout = new Flyout();
	
	flyout.setParent(Element.get("wsod"));
	flyout.addTarget(Element.get("reportSelectRDA"));
	flyout.setData(this.getReports());
	flyout.onSelect().addListener({
		context: this,
		handler: this.selectReport
	});
	
	var flyoutLabel = Element.get("reportSelectRDA").firstChild;
	var flyoutControls = Element.parseSelector("ul li", flyout.getFrame());

	this.getReportFlyoutLabel = function() {
		return flyoutLabel;
	}
	
	this.getReportFlyoutControls = function() {
		return flyoutControls;
	}
	
	this.getReportFlyout = function() {
		return flyout;
	}
	
	return this.getReportFlyout();
}

ResearchDataArchive.prototype.selectCategory = function(e, item) {
	Element.setHTML(this.getCategoryFlyoutLabel(), item.getLabel());
	this.updateReports(item.getValue());
	
	//set report back to default
	var reportTitle = Element.parseSelector("div", "reportSelectRDA");
	Element.setHTML(reportTitle, "- Select Report -");
	
	//remove report link
	var reportLink = Element.parseSelector("div.reportLink a", "researchDataArchiveModule", "first")
	
	if(reportLink == null){
		return;
	}else{
		Element.remove(reportLink);
	}
}

ResearchDataArchive.prototype.updateReports = function(val) {
	var flyoutControls = this.getReportFlyoutControls();
	
	for (var i = 0; i < flyoutControls.length; i++) {
		if ((!Element.hasClass(flyoutControls[i], val) && !Element.hasClass(flyoutControls[i], this.REPORT_TYPE_ALL)) || !val) {
			Element.addClass(flyoutControls[i], this.CSS_HIDDEN);
		} else {
			Element.removeClass(flyoutControls[i], this.CSS_HIDDEN);
		}
	}
}

ResearchDataArchive.prototype.selectReport = function(e, item) {
	Element.setHTML(this.getReportFlyoutLabel(), item.getLabel());
	
	this.setReportType(item.getValue());
	
	if (this.getCalendarContainer()) {
		Element.removeClass(this.getCalendarContainer(), "inactive");
		this.loadCalendar();
	} else {
		this.loadReport();
	}
}

ResearchDataArchive.prototype.drawReportLink = function(cb) {
	var data = this.getSerializer().deserialize(cb.getResult());
	var reportLink = Element.create("a", {"target":"_blank", "href":"/ft/markets/reports/FTReport.asp?dockey=" + data[0].DocumentTag + "-" + data[0].VersionTag}, "View Report", this.getReportLinkContainer());
	
	if (data && data.length) {
		reportLink
	} else {
		alert("Report of the requested type is unavailable at this time.");
	}
	
}

ResearchDataArchive.prototype.getReportLinkContainer = function() {
	var reportLinkContainer = Element.parseSelector("div.reportLink", "researchDataArchiveModule", "first");
	
	this.getReportLinkContainer = function() {
		return reportLinkContainer;
	}
	
	return this.getReportLinkContainer();
}

ResearchDataArchive.prototype.pageCalendar = function(e, el) {
	var month = Number(el.getAttribute("month"));
	var year = Number(el.getAttribute("year"));
	var today = new Date();
	

	if (year > today.getFullYear() || (year == today.getFullYear() && month > today.getMonth())){ 
		return; 
	}
	
	
	
	this.setDate((month + 1) + "/1/" + year);
	
	this.loadCalendar();
}

ResearchDataArchive.prototype.loadCalendar = function() {
	this.getContentBuffer().abortRequests();
	this.getContentBuffer().load({
		url: "/ft/resources/buffer/getResearchDataArchiveCalendar.asp",
		contentType: "text/html",
		data: {
			targetDate: this.getDate(),
			reportType: this.getReportType()
		},
		onload: this.drawCalendar,
		context: this
	});
}

ResearchDataArchive.prototype.loadReport = function() {
	Element.removeChildNodes(this.getReportLinkContainer());

	this.getContentBuffer().abortRequests();
	this.getContentBuffer().load({
		url: "/ft/resources/buffer/getResearchDataArchiveRecentReport.asp",
		contentType: "text/javascript",
		preventEval: true,
		data: {
			reportType: this.getReportType()
		},
		onload: this.drawReportLink,
		context: this
	});
}

ResearchDataArchive.prototype.drawCalendar = function(cb) {
	Element.setHTML(this.getCalendarContainer(), cb.getResult());
	
	this.getEventManager().add(Element.parseSelector("a[year]", this.getCalendarContainer()), "click", this.pageCalendar, this);
}

ResearchDataArchive.prototype.getReportType = function() { return ""; }
ResearchDataArchive.prototype.setReportType = function(r) {
	this.getReportType = function() {
		return r;
	}
}

ResearchDataArchive.prototype.getDate = function() {
	var el = Element.parseSelector("div.calendarCurrentDate", this.getCalendarContainer(), "first");
	
	
	var month = Number(el.getAttribute("month"));
	var year = Number(el.getAttribute("year"));
	var today = new Date();

	if (year > today.getFullYear() || (year == today.getFullYear() && month > today.getMonth())) { return ""; }
	
	var d = (month + 1) + "/1/" + year;
	
	return d;
}

ResearchDataArchive.prototype.setDate = function(d) {
	this.getDate = function() {
		return d;
	}
}

ResearchDataArchive.prototype.getEventManager = function() {
	var em = new EventManager();
	
	this.getEventManager = function() {
		return em;
	}
	
	return this.getEventManager();
}

ResearchDataArchive.prototype.getContentBuffer = function() {
	var cb = new ContentBuffer();
	
	this.getContentBuffer = function() {
		return cb;
	}
	
	return this.getContentBuffer();
}

ResearchDataArchive.prototype.getSerializer = function() {
	var s = new Serializer();
	
	this.getSerializer = function() {
		return s;
	}
	
	return this.getSerializer();
}
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/Flyout.js
*/
var Flyout = function() {
	
};

Flyout.clearVisibleFlyouts = function() {
	if (!this.visibles) {
		return;
	}
	for (var i=0; i<this.visibles.length; i++) {
		this.visibles[i].hide();
	}
	this.visibles = [];
};

Flyout.addVisibleFlyout = function(flyout) {
	this.visibles = this.visibles || [];
	this.visibles.push(flyout);
};


Flyout.prototype.CSS_HIDDEN = "wsodHidden";
Flyout.prototype.CSS_CONTAINER = "flyout";
Flyout.prototype.CSS_SELECTED = "selected";
Flyout.prototype.CSS_SHADOW = "shadow";
Flyout.prototype.CSS_SUB = "sub";

Flyout.prototype.POSITION_BELOW = "below";
Flyout.prototype.POSITION_RIGHT = "right";

Flyout.prototype.addTarget = function(el) {
	this.getShowEvent().addElement(el);
};

Flyout.prototype.getEventManager = function() {
	var em = new EventManager();
	this.getEventManager = function() {
		return em;
	};
	return this.getEventManager();
};

Flyout.prototype.getShowEvent = function() {
	var showEvent = Events.add(null, "mouseup", this.show, this);
	this.getShowEvent = function() {
		return showEvent;
	};
	return this.getShowEvent();
};


Flyout.prototype.getHideEvent = function() {
	var hideEvent = Events.add(null, "mouseout", this.hide, this, null, 1000);
	this.getHideEvent = function() {
		return hideEvent;
	};
	return this.getHideEvent();
};

Flyout.prototype.getPageClickEvent = function() {
	var e = Events.add(null, "mouseup", this.hide, this);
	this.getPageClickEvent = function() {
		return e;
	};
	return this.getPageClickEvent();
};

Flyout.prototype.getMouseOver = function() {
	var mouseOver = Events.add(null, "mouseover", this.show, this);
	this.getMouseOver = function() {
		return mouseOver;
	};
	return this.getMouseOver();
};

Flyout.prototype.getParent = function() {
	return WSDOM.Element.get("wsod");
};

Flyout.prototype.setParent = function(el) {
	this.getParent = function() {
		return el;
	};
};

Flyout.prototype.getFlyoutParent = function() {
	return null;
};

Flyout.prototype.setFlyoutParent = function(flyout) {

	this.setParent(flyout.getContainer());
	this.setPositionTarget(flyout.getContainer());
	
	this.getFlyoutParent = function() {
		return flyout;
	};
	
	WSDOM.Element.addClass(this.getFrame(), this.CSS_SUB);
};

Flyout.prototype.getFrame = function() {
	var frame = WSDOM.Element.create('div', { className: this.CSS_CONTAINER }, [
		WSDOM.Element.create("iframe", { "class": this.CSS_SHADOW, src: "javascript:false;", frameborder: 0 }),
		WSDOM.Element.create("div", { "class": this.CSS_SHADOW }),
		WSDOM.Element.create("ul")
	], this.getParent());
	
	WSDOM.Element.addClass(frame, this.CSS_HIDDEN);
	
	this.getFrame = function() {
		return frame;
	};
	
	return this.getFrame();
};

Flyout.prototype.getShim = function() {
	return this.getFrame().childNodes[0];
};

Flyout.prototype.getShadow = function() {
	return this.getFrame().childNodes[1];
};

Flyout.prototype.getContainer = function() {
	var container = this.getFrame().childNodes[2];
	this.getMouseOver().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	return this.getContainer();
};

Flyout.prototype.setData = function(data) {
	this.data = data;
	this.items = [];
	this.itemsById = {};
	
	var container = this.getContainer();
	
	WSDOM.Element.removeChildNodes(container);

	for (var i=0; i<data.length; i++) {
		
		var flyoutItem = new FlyoutItem(this);
		flyoutItem.setData(data[i]);
		flyoutItem.isLast((i == data.length-1));
		this.items.push(flyoutItem);
		
		if (data[i].id) {
			this.itemsById[data[i].id] = flyoutItem;
		}
		
	};
	
};

Flyout.prototype.isVisible = function() {
	return !WSDOM.Element.hasClass(this.getFrame(), this.CSS_HIDDEN);
}

Flyout.prototype.show = function(e, el) {	
	e.cancel();
	
	this.getHideEvent().clearDelayTimeouts();
	
	if (this.isVisible()) {	
	
		if (el == this.currentTarget) {
			this.currentTarget = null;
			this.hide();
		}
		
		return;
	}
	
	if (!this.getFlyoutParent()) {
		this.currentTarget = el;
		Flyout.clearVisibleFlyouts();
		Flyout.addVisibleFlyout(this);
	}
	
	var frame = this.getFrame();
	
	this.getHideEvent().addElement([frame, el]);
	this.getPageClickEvent().addElement(document);
	
	this.deselectAll();
	
	WSDOM.Element.removeClass(frame, this.CSS_HIDDEN);
	this.position(el);
};

Flyout.prototype.position = function(elTarget) {
	var frame = this.getFrame();
	WSDOM.Element.setStyle(frame, "visibility: hidden");
	
	var target = this.getPositionTarget() || elTarget;
	
	var targetPos = WSDOM.Element.getXY(target);
	var targetSize = WSDOM.Element.getSize(target);
	
	var size = WSDOM.Element.getSize(this.getFrame());
	var offset = WSDOM.Element.getXY(this.getFrame().offsetParent) || { x: 0, y: 0 };
	
	var x = targetPos.x - offset.x;
	var y = targetPos.y - offset.y;
	
	if (this.POSITION_BELOW == this.getPositionStyle()) {
		y += targetSize.height;
	}
	else if (this.POSITION_RIGHT == this.getPositionStyle()) {
		x += targetSize.width;
		y = this.checkVerticalPos(y, elTarget, size, offset);
	}
	
	WSDOM.Element.setXY(
		this.getFrame(), x, y
	);
	
	this.sizeShim(size);
	
	WSDOM.Element.setStyle(frame, "visibility: visible");
};

Flyout.prototype.sizeShim = function(size) {
	// because i can't figure out how to make IE6 do this on its own
	// with the magic of CSS (it's ignoring height 100%)
	// laaaaaaame
	WSDOM.Element.setSize(this.getShim(), size.width, size.height);
	WSDOM.Element.setSize(this.getShadow(), size.width, size.height);
};

Flyout.prototype.checkVerticalPos = function(y, elTarget, size, offset) {
	var elTargetSize = WSDOM.Element.getSize(elTarget);
	var elTargetPos = WSDOM.Element.getXY(elTarget);
	var elTargetBottom = elTargetPos.y + elTargetSize.height - offset.y;
	
	if (elTargetBottom > y + size.height) {
		y = elTargetBottom - size.height;
	}
	
	return y;
};

Flyout.prototype.getPositionStyle = function() {
	return this.POSITION_BELOW;
};

Flyout.prototype.setPositionStyle = function(style) {
	this.getPositionStyle = function() {
		return style
	};
};

Flyout.prototype.getPositionTarget = function() {
	return null;
};

Flyout.prototype.setPositionTarget = function(el) {
	this.getPositionTarget = function() {
		return el;
	};
};

Flyout.prototype.hide = function(e, el) {
	WSDOM.Element.addClass(this.getFrame(), this.CSS_HIDDEN);
	
	//frieken ie6
	this.getFrame().style.visibility = "hidden";
	
	this.hideChildren();
	
	this.getHideEvent().removeAllElements();
	this.getPageClickEvent().removeAllElements();
};

Flyout.prototype.deselectAll = function() {
	for (var i=0; i<this.items.length; i++) {
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
	};
}

Flyout.prototype.hideChildren = function(item) {
	for (var i=0; i<this.items.length; i++) {
		if (item == this.items[i]) {
			continue;
		}
		
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
		
		if (this.items[i].child) {
			this.items[i].child.hide();
		}
	}
};

Flyout.prototype.hover = function(item) {
	this.getHideEvent().clearDelayTimeouts();
	this.deselectAll();
	this.hideChildren();

	var parent = this;
	while (parent = parent.getFlyoutParent()) {
		parent.getHideEvent().clearDelayTimeouts();
	}
};

Flyout.prototype.onSelect = function() {
	var event = this.getEventManager().add("flyoutItemSelected");
	this.onSelect = function() {
		return event;
	};
	
	this.onSelect().addListener(function(e, item) {
		
		
		if (item.child) {
			// we do nothing on click
			// prevent propagation?
			return;
		}
		
		//console.log("firing", e, item)
		this.hide();
		
		// do I have to fire the handlers all the way up the tree?
		//var parent = this;
		//while (parent = parent.getFlyoutParent()) {
		
		var parent = this.getFlyoutParent();
		
		if (parent) {
			parent.onSelect().fire(item);
			parent.hide();
		}
		//}
			
	}, this);
	
	return this.onSelect();
};





// ------------------------------------------------------------
// ------------------------------------------------------------
// ------------------------------------------------------------


var FlyoutItem = function(flyout) {
	if (!flyout) {
		throw new Error("FlyoutItem Constructor: flyout is required");
	}
	this.flyout = flyout;
};

FlyoutItem.prototype.CSS_HAS_CHILD = "icon icon-arrow-right";
FlyoutItem.prototype.CSS_LAST = "last";

FlyoutItem.prototype.getHoverEvent = function() {
	var hover = this.flyout.getEventManager().add(null, "mouseover", this.hover, this);
	this.getHoverEvent = function() {
		return hover;
	};
	return this.getHoverEvent();
};

FlyoutItem.prototype.hover = function(e) {
	e.cancel();
	
	this.flyout.hover(this);
	
	if (this.child) {
		this.child.getShowEvent().fire(this.getContainer());
		//this.show()
	}
	
	WSDOM.Element.addClass(this.getContainer(), this.flyout.CSS_SELECTED);
	
	this.flyout.sizeShim(WSDOM.Element.getSize(this.flyout.getContainer()))
};

FlyoutItem.prototype.getSelectEvent = function() {
	var event = this.flyout.getEventManager().add(null, "click", this.select, this);
	
	this.getSelectEvent = function() {
		return event;
	}
	
	return this.getSelectEvent();
};

FlyoutItem.prototype.getLabel = function() {
	return null;
};

FlyoutItem.prototype.setLabel = function(label) {
	this.getLabel = function() {
		return label;
	};
	
	this.getContainer().innerHTML = label;
};

FlyoutItem.prototype.getValue = function() {
	return null;
};

FlyoutItem.prototype.setValue = function(value) {
	this.getValue = function() {
		return value;
	}
	
	if (undefined === value) {
		value = "";
	}
	
	this.getContainer().setAttribute("value", value);
};

FlyoutItem.prototype.getContainer = function(data) {
	var container = WSDOM.Element.create("li", null, null, this.flyout.getContainer());
	
	// this belongs somewhere else
	if (data.data) {
		for (var i in data.data) {
			container.setAttribute("data." + i, data.data[i]);	
		}
	}
	
	// this belongs somewhere else
	if (data.css) {
		WSDOM.Element.addClass(container, data.css);
	}
	
	
	this.getHoverEvent().addElement(container);
	this.getSelectEvent().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	this.setLabel(data.label);
	this.setValue(data.value);
	
	return this.getContainer();
};

FlyoutItem.prototype.setData = function(data) {
	this.data = data;
	
	var container = this.getContainer(data);
	
	if (this.child) {
		WSDOM.Element.remove(this.child.getFrame());
		WSDOM.Element.removeClass(container, this.CSS_HAS_CHILD);
		delete this.child;
	}
	
	
	if (data.children) {
		//console.log(data)
		var f = new Flyout();

		f.setFlyoutParent(this.flyout);
		f.setPositionStyle(f.POSITION_RIGHT);

		f.setData(data.children);
		
		f.addTarget(container);
		
		// this belongs somewhere else
		if (data.css) {
			WSDOM.Element.addClass(f.getFrame(), data.css);
		}
		
		WSDOM.Element.addClass(container, this.CSS_HAS_CHILD);
		
		this.child = f;
	}
};

FlyoutItem.prototype.isLast = function(isLast) {
	WSDOM.Element.switchClass(this.getContainer(), this.CSS_LAST, isLast);
};

FlyoutItem.prototype.select = function() {
	this.flyout.onSelect().fire(this);
};
	